
# *** author zhaomin
# 认证相关的依赖注入和装饰器
# 支持灵活的认证控制
# """

from typing import Optional, Callable, Any
from functools import wraps
from fastapi import HTTPException, status, Depends, Request
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from starlette.responses import JSONResponse

from app.core.config import settings
from app.utils.user import verify_token
from app.schemas.base.login import JWTPayload
from app.core.user import CTX_USER_ID
from app.utils.request import RequestHandler


# HTTP Bearer 安全方案
security = HTTPBearer(auto_error=False)


async def get_current_user_optional(
    request: Request,
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(security)
) -> Optional[JWTPayload]:
    """
    可选的用户认证依赖
    如果认证失败或未提供token，返回None而不是抛出异常
    
    Args:
        request: FastAPI请求对象
        credentials: HTTP Bearer 凭证
    Returns:
        Optional[JWTPayload]: 用户信息，认证失败时返回None
    """
    # 如果全局禁用认证，直接返回None
    if not settings.enable_auth:
        return None
    
    # 检查路径是否在白名单中
    if request.url.path in settings.auth_whitelist:
        return None
        
    if not credentials:
        return None
        
    token = credentials.credentials
    user_payload = verify_token(token)
    
    if user_payload:
        # 设置用户上下文
        CTX_USER_ID.set(user_payload.user_id)
        
    return user_payload



async def get_current_user_required(
    request: Request,
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(security)
) -> JWTPayload:
    """
    必需的用户认证依赖
    如果认证失败会抛出HTTP异常
    
    Args:
        request: FastAPI请求对象
        credentials: HTTP Bearer 凭证
        
    Returns:
        JWTPayload: 用户信息
        
    Raises:
        HTTPException: 认证失败时抛出401异常
    """
    # 如果全局禁用认证，返回一个默认用户
    if not settings.enable_auth:
        return JWTPayload(
            user_id=0,
            username="anonymous",
            is_superuser=False,
            exp=None
        )
    
    # 检查路径是否在白名单中
    if request.url.path in settings.auth_whitelist:
        return JWTPayload(
            user_id=0,
            username="anonymous", 
            is_superuser=False,
            exp=None
        )
    
    if not credentials:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="缺少认证凭证"
        )
        
    token = credentials.credentials
    user_payload = verify_token(token)
    
    if not user_payload:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="无效的token或token已过期"
        )
    
    # 设置用户上下文
    CTX_USER_ID.set(user_payload.user_id)
    
    return user_payload


async def get_current_superuser(
    current_user: JWTPayload = Depends(get_current_user_required)
) -> JWTPayload:
    """
    超级用户认证依赖
    
    Args:
        current_user: 当前用户信息
        
    Returns:
        JWTPayload: 超级用户信息
        
    Raises:
        HTTPException: 如果用户不是超级用户，抛出403异常
    """
    if not current_user.is_superuser:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="权限不足，需要超级用户权限"
        )
    return current_user


def auth_required(func: Callable) -> Callable:
    """
    认证装饰器，用于强制要求认证的路由
    即使全局禁用认证，使用此装饰器的路由仍需要认证
    
    Args:
        func: 要装饰的函数
        
    Returns:
        Callable: 装饰后的函数
    """
    @wraps(func)
    async def wrapper(*args, **kwargs):
        # 从kwargs中获取request对象
        request = None
        for arg in args:
            if hasattr(arg, 'url') and hasattr(arg, 'method'):
                request = arg
                break
        
        if not request:
            # 如果没有找到request对象，从kwargs中查找
            request = kwargs.get('request')
        
        if not request:
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="内部错误：无法获取请求对象"
            )
        
        # 获取Authorization header
        auth_header = request.headers.get("Authorization")
        if not auth_header or not auth_header.startswith("Bearer "):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="缺少认证凭证"
            )
        
        token = auth_header.split(" ")[1]
        user_payload = verify_token(token)
        
        if not user_payload:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="无效的token或token已过期"
            )
        
        # 设置用户上下文
        CTX_USER_ID.set(user_payload.user_id)
        
        # 将用户信息注入到kwargs中
        kwargs['current_user'] = user_payload
        
        return await func(*args, **kwargs)
    
    return wrapper


def auth_optional(func: Callable) -> Callable:
    """
    可选认证装饰器，用于可选认证的路由
    如果提供了有效token则解析用户信息，否则继续执行
    
    Args:
        func: 要装饰的函数
        
    Returns:
        Callable: 装饰后的函数
    """
    @wraps(func)
    async def wrapper(*args, **kwargs):
        # 从kwargs中获取request对象
        request = None
        for arg in args:
            if hasattr(arg, 'url') and hasattr(arg, 'method'):
                request = arg
                break
        
        if not request:
            request = kwargs.get('request')
        
        current_user = None
        
        if request:
            # 获取Authorization header
            auth_header = request.headers.get("Authorization")
            if auth_header and auth_header.startswith("Bearer "):
                token = auth_header.split(" ")[1]
                user_payload = verify_token(token)
                if user_payload:
                    current_user = user_payload
                    CTX_USER_ID.set(user_payload.user_id)
        
        # 将用户信息注入到kwargs中（可能为None）
        kwargs['current_user'] = current_user
        
        return await func(*args, **kwargs)
    
    return wrapper